/*
 * @Date         : 2024-05-21 21:57:46 星期2
 * @Author       : xut
 * @Description  : Nest 以两种可能的方式支持 GRPC 流处理程序：
 * 1. @GrpcStreamMethod 装饰器，配合 RxJS Subject + Observable 处理程序：可用于在 Controller 方法内部编写响应或传递给 Subject/Observable 消费者，例子见 hero 模块的 hero-service.controller.ts
 * 2. @GrpcStreamCall 装饰器，纯 GRPC 调用流处理程序：传递给某个执行程序可能很有用，该执行程序将处理 Node 标准 Duplex 流处理程序的其余分派。例子见 hello 模块的 hello-service.controller.ts
 */
import { Controller } from '@nestjs/common';
import { GrpcMethod, GrpcStreamMethod } from '@nestjs/microservices';
import { HeroById } from './interface/hero/HeroById';
import { Hero } from './interface/hero/Hero';
import { Observable, Subject } from 'rxjs';
import { Metadata, ServerDuplexStream, ServerUnaryCall } from '@grpc/grpc-js';

@Controller()
export class HeroServiceController {
  private readonly items: Hero[] = [
    { id: 1, name: 'John' },
    { id: 2, name: 'Doe' },
  ];

  @GrpcMethod('HeroService', 'FindOne')
  findOne(
    data: HeroById,
    metadata: Metadata,
    unaryCall: ServerUnaryCall<HeroById, Hero>,
  ): Hero {
    // 一元模式读取客户端发送的元数据
    const unaryClientMetadata = metadata.get('unary-client-metadata');
    console.log(
      '🚀 ~ HeroServiceController ~ unaryClientMetadata:',
      unaryClientMetadata,
    );

    // 一元模式添加元数据
    const serverMetadata = new Metadata();
    serverMetadata.add('unary-metadata', 'custom-metadata-value');
    unaryCall.sendMetadata(serverMetadata);

    return this.items.find((i) => i.id === data.id);
  }

  @GrpcStreamMethod('HeroService', 'FindMany')
  findMany(
    data$: Observable<HeroById>,
    metadata: Metadata,
    bidiStreamCall: ServerDuplexStream<HeroById, Hero>,
  ): Observable<Hero> {
    const duplexClientMetadata = metadata.get('bidi-client-metadata');
    console.log(
      '🚀 ~ HeroServiceController ~ findMany ~ duplexClientMetadata:',
      duplexClientMetadata,
    );

    const serverMetadata = new Metadata();
    serverMetadata.add('bidi-server-metadata', 'bidi-server-metadata');
    bidiStreamCall.sendMetadata(serverMetadata);

    const hero$ = new Subject<Hero>();
    const onNext = (heroById: HeroById) => {
      const item = this.items.find((i) => i.id === heroById.id);
      hero$.next(item);
    };

    const onComplete = () => hero$.complete();

    data$.subscribe({ next: onNext, complete: onComplete });

    return hero$.asObservable();
  }
}
